﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Anagramme.Model;
using System.Windows.Input;
using System.ApplicationModel;
using Anagramme.Views;
using System.ComponentModel.Composition;
using Anagramme.Contracts;
using System.ComponentModel.DataAnnotations;
using System.ApplicationModel.Composition;
using System.ServiceModel;
using System.ComponentModel.Composition.Hosting;
using System.Windows;
using System.Globalization;
using Anagramme.Services;
using System.Diagnostics;
using System.Activities;
using Anagramme.Activities.BusinessRules;

namespace Anagramme.ViewModels
{
    /// <summary>
    /// ViewModel de la connexion à un lobby
    /// </summary>
    [ViewModelExport(Modules.JoinGame)]
    [PartCreationPolicy(System.ComponentModel.Composition.CreationPolicy.NonShared)]
    public class JoinGameViewModel : ViewModelBase<ILobbyModel>
    {
        #region Contructors
        /// <summary>
        /// Crée une nouvelle instance de la classe <see cref="JoinGameViewModel"/>
        /// </summary>
        public JoinGameViewModel()
        {
#if DEBUG
            this.ServerIP = "localhost";
#endif
        }
        #endregion

        #region Fields
        private string _serverIP;

        private ICommand _goBackCommand;
        private ICommand _connectCommand;
        #endregion

        #region Public Properties
        /// <summary>
        /// Obtient ou défini l'IP du serveur auquel se connecter
        /// </summary>
        [Required]
        public string ServerIP
        {
            get { return this._serverIP; }
            set
            {
                if (this._serverIP != value)
                {
                    base.RaisePropertyChanging();
                    this._serverIP = value;
                    base.TraceAssign(value);
                    base.RaisePropertyChanged();
                }
            }
        }
        #endregion

        #region Commands
        /// <summary>
        /// Obtient la commande pour retourner à la page d'accueil
        /// </summary>
        public ICommand GoBackCommand
        {
            get
            {
                if (object.ReferenceEquals(this._goBackCommand, null))
                {
                    this._goBackCommand = new DelegateCommand(this.GoBack, this.CanGoBack);
                }
                return this._goBackCommand;
            }
        }

        /// <summary>
        /// Obtient la commande pour se connecter au lobby
        /// </summary>
        public ICommand ConnectCommand
        {
            get
            {
                if (object.ReferenceEquals(this._connectCommand, null))
                {
                    this._connectCommand = new DelegateCommand(this.Connect, this.CanConnect);
                }
                return this._connectCommand;
            }
        }
        #endregion

        #region Model callbacks
        private void Model_OnResponseLobbyJoin(object sender, EventArgs<Guid> e)
        {
            ApplicationServices.Trace.TraceMethodEntering();

            WeakEventManager<ILobbyModel, EventArgs<Guid>>.RemoveHandler(base.Model, "OnResponseLobbyJoin", this.Model_OnResponseLobbyJoin);

            Guid token = e.Value;

            if (!token.Equals(Guid.Empty))
            {
                IResultsModel resultsModel = ApplicationServices.Container.GetExportedValue<IResultsModel>();
                IGameModel gameModel = ApplicationServices.Container.GetExportedValue<IGameModel>();

                resultsModel.Authenticate(token);
                gameModel.Authenticate(token);

                base.BeginWaitingAction();
                WeakEventManager<ILobbyModel, EventArgs<Player>>.AddHandler(base.Model, "OnResponseLobbyPlayerInfo", this.Model_OnResponseLobbyPlayerInfo);
                this.Model.RequestLobbyPlayerInfo();
            }
            else
            {
                ApplicationServices.Message.ShowMessage(Resources.JoinGameStrings.SERVER_FULL);
            }
            base.EndWaitingAction();

            ApplicationServices.Trace.TraceMethodLeaving();
        }

        private void Model_OnResponseLobbyPlayerInfo(object sender, EventArgs<Player> e)
        {
            ApplicationServices.Trace.TraceMethodEntering();

            WeakEventManager<ILobbyModel, EventArgs<Player>>.RemoveHandler(base.Model, "OnResponseLobbyPlayerInfo", this.Model_OnResponseLobbyPlayerInfo);
            base.Player = e.Value;
            base.EndWaitingAction();

            ApplicationServices.ViewWorkflow.SendMessage(Messages.JoinGameConnect);

            ApplicationServices.Trace.TraceMethodLeaving();
        }
        #endregion

        #region Methods
        private bool CanGoBack()
        {
            ApplicationServices.Trace.TraceMethodEntering();

            Dictionary<string, object> arguments = new Dictionary<string, object>()
            {
                { "IsBusy", base.IsBusy }
            };

            bool returnValue = (bool)WorkflowInvoker.Invoke(new NotBusyActivity(), arguments)["ReturnValue"];
            
            ApplicationServices.Trace.TraceReturnValue(returnValue); 
            ApplicationServices.Trace.TraceMethodLeaving();
            return returnValue;
        }
        private void GoBack()
        {
            ApplicationServices.Trace.TraceMethodEntering();

            ApplicationServices.ViewWorkflow.SendMessage(Messages.JoinGameGoBack);

            ApplicationServices.Trace.TraceMethodLeaving();
        }

        private bool CanConnect()
        {
            ApplicationServices.Trace.TraceMethodEntering();

            Dictionary<string, object> arguments = new Dictionary<string, object>()
            {
                { "IsBusy", base.IsBusy },
                { "HasErrors", this.HasErrors }
            };

            bool returnValue = (bool)WorkflowInvoker.Invoke(new NotBusyNoErrorsActivity(), arguments)["ReturnValue"];
            
            ApplicationServices.Trace.TraceReturnValue(returnValue); 
            ApplicationServices.Trace.TraceMethodLeaving();
            return returnValue;
        }
        private void Connect()
        {
            ApplicationServices.Trace.TraceMethodEntering();

            base.BeginWaitingAction();

            ILobbyModel lobbyModel = ApplicationServices.Container.GetExportedValue<ILobbyModel>();
            IResultsModel resultsModel = ApplicationServices.Container.GetExportedValue<IResultsModel>();
            IGameModel gameModel = ApplicationServices.Container.GetExportedValue<IGameModel>();

            try
            {
                lobbyModel.Connect(string.Format(CultureInfo.InvariantCulture, "net.tcp://{0}:20899/anagramme/lobby", this.ServerIP));
                resultsModel.Connect(string.Format(CultureInfo.InvariantCulture, "net.tcp://{0}:20899/anagramme/results", this.ServerIP));
                gameModel.Connect(string.Format(CultureInfo.InvariantCulture, "net.tcp://{0}:20899/anagramme/game", this.ServerIP));

                WeakEventManager<ILobbyModel, EventArgs<Guid>>.AddHandler(base.Model, "OnResponseLobbyJoin", this.Model_OnResponseLobbyJoin);
                base.Model.RequestLobbyJoin(Properties.Settings.Default.PlayerName);
            }
            catch
            {
                ApplicationServices.Message.ShowWarning(Resources.JoinGameStrings.CONNECTION_FAILED);
                base.EndWaitingAction();
            }

            ApplicationServices.Trace.TraceMethodLeaving();
        }
        #endregion
    }
}
